﻿using EasyFileSync.Core.Client;
using EasyFileSync.Core.Config;
using EasyFileSync.Core.Entity;
using EasyFileSync.Core.Enums;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EasyFileSync.Core
{
    public class SyncJob : IDisposable
    {
        public delegate void Output(string text);
        public event Output OutStream;

        public JobConfig Config { get; internal set; }
        public SyncJob(JobConfig config) => this.Config = config;

        public virtual void Start()
        {
            if (Config == null)
                throw new Exception("Config 未初始化。");

            var sw = new Stopwatch();
            sw.Start();
            Sync(Config.From, Config.To);
            sw.Stop();
            Print($"同步完成，耗时 {sw.Elapsed.TotalMilliseconds/3600000}H ({sw.Elapsed.TotalMilliseconds}ms).");
        }

        protected void Sync(ISyncDir src, ISyncDir tar)
        {
            var fileComparer = new SyncFileComparer();
            var srcFiles = src.GetFiles();
            var tarFiles = tar.GetFiles();

            var newFiles = srcFiles.Except(tarFiles, fileComparer);// 源有目标没有，新增
            Parallel.ForEach(newFiles, file =>
            {
                CopyTo(file, tar);

                var tarPath = Path.Combine(tar.FullName, file.Name);
                Print($"新增文件：{tarPath}");
            });

            if (Config.Mode == SyncMode.Mirror)
            {
                var delFiles = tarFiles.Except(srcFiles, fileComparer);// 源没有目标有，删除
                Parallel.ForEach(delFiles, file =>
                {
                    file.Delete();
                    Print($"删除文件：{file.FullName}");
                });


                var commonFiles = srcFiles.Intersect(tarFiles, fileComparer);// 源有目标有，对比更新
                Parallel.ForEach(commonFiles, file =>
                {
                    var temp = tarFiles.FirstOrDefault(f => f.Name == file.Name);
                    if (Equals(file, temp, Config.Strategy))
                        return;

                    CopyTo(file, tar);

                    var tarPath = Path.Combine(tar.FullName, file.Name);
                    Print($"更新文件：{tarPath}");
                });
            }

            // 继续遍历文件夹
            var dirComparer = new SyncDirComparer();
            var srcDirs = src.GetDirs();
            var tarDirs = tar.GetDirs();

            var newDirs = srcDirs.Except(tarDirs, dirComparer);// 源有目标没有，新增
            Parallel.ForEach(newDirs, dir =>
            {
                CopyTo(dir, tar);

                var tarPath = Path.Combine(tar.FullName, dir.Name);
                Print($"新增文件夹：{tarPath}");
            });

            if (Config.Mode == SyncMode.Mirror)
            {
                // 源没有目标有，删除
                var delDirs = tarDirs.Except(srcDirs, dirComparer);
                Parallel.ForEach(delDirs, dir =>
                {
                    dir.Delete();
                    Print($"删除文件夹：{dir.FullName}");
                });

                // 源有目标有，递归对比
                var commonDirs = srcDirs.Intersect(tarDirs, dirComparer);
                Parallel.ForEach(commonDirs, dir =>
                {
                    var temp = tarDirs.FirstOrDefault(f => f.Name == dir.Name);
                    Sync(dir, temp);
                });
            }
        }

        protected void CopyTo(ISyncDir src, ISyncDir tarParent)
        {
            var tarPath = Path.Combine(tarParent.FullName, src.Name);
            var tarDir = tarParent is NTFSDir ? (ISyncDir)new NTFSDir(tarPath) : new FtpDir((tarParent as FtpDir).Config, tarPath);

            if (!Directory.Exists(tarDir.FullName))
                Directory.CreateDirectory(tarDir.FullName);

            Sync(src, tarDir);
            //src.CopyTo(tarDir);
        }
        protected void CopyTo(ISyncFile src, ISyncDir tarParent)
        {
            var tarPath = Path.Combine(tarParent.FullName, src.Name);
            var tarFile = tarParent is NTFSDir ? (ISyncFile)new NTFSFile(tarPath) : new FtpFile((tarParent as FtpDir).Config, tarPath);

            src.CopyTo(tarFile);
        }

        protected bool Equals(ISyncFile src, ISyncFile tar, SyncStrategy strategy = SyncStrategy.Date)
        {
            switch (strategy)
            {
                case SyncStrategy.Size:
                    return src.Size == tar.Size;
                case SyncStrategy.HashCode:
                    return src.HashCode == tar.HashCode;
                case SyncStrategy.Date:
                    var dateFormat = "yyyy-MM-dd hh:mm:ss";
                    if (src is FtpFile || tar is FtpFile)
                        dateFormat = "yyyy-MM-dd hh:mm";
                    return src.GetModifyDate(dateFormat) == tar.GetModifyDate(dateFormat);
            }

            return false;
        }
        protected void Print(string output) => OutStream?.Invoke(output);
        public virtual void Dispose() { }
    }

}
